
#include "types.h"
#include "riscv.h"
#include "config.h"
#include "htif.h"
#include "mtrap.h"
#include "file.h"
#include "fdt.h"
#include "string.h"


// entry.S needs one stack per CPU.
__attribute__ ((aligned (16))) char stack0[4096 * NCPU];
uintptr_t mem_size;
volatile uint64* mtime;




void init_dtb(uint64 dtb){
    query_htif(dtb);
    printm("PKE IS RUNNING\r\n");
    query_mem(dtb);
    printm("mem_size %p\n",mem_size);
    hls_t *hart0_hls=hls_init(0);
    query_harts(dtb);
    query_clint(dtb);
    // hls_print(hart0_hls);
}

void main();
// entry.S jumps here in machine mode on stack0.



// send S-mode interrupts and most exceptions straight to S-mode
static void delegate_traps()
{

  uintptr_t interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
  uintptr_t exceptions =
    (1U << CAUSE_MISALIGNED_FETCH) |
    (1U << CAUSE_FETCH_PAGE_FAULT) |
    (1U << CAUSE_BREAKPOINT) |
    (1U << CAUSE_LOAD_PAGE_FAULT) |
    (1U << CAUSE_STORE_PAGE_FAULT) |
    (1U << CAUSE_USER_ECALL);
  write_csr(mideleg, interrupts);
  if(DEBUG_CSR){
    printm("write %p to medeleg \n",exceptions);
  }
  write_csr(medeleg, exceptions);
  if(DEBUG_CSR|DEBUG){
    printm("medeleg value after write %p\n",read_csr(medeleg));
    printm("mideleg value after write %p\n",read_csr(mideleg));
  }

  assert(read_csr(mideleg) == interrupts);
  assert(read_csr(medeleg) == exceptions);
}
void  mtrapvec();


// set up to receive timer interrupts in machine mode,
// which arrive at timervec in kernelvec.S,
// which turns them into software interrupts for
// devintr() in trap.c.
// a scratch area per CPU for machine-mode timer interrupts.
uint64 timer_scratch[NCPU][5];
void
timerinit()
{
  // each CPU has a separate source of timer interrupts.
  int id = r_mhartid();

  // ask the CLINT for a timer interrupt.
  int interval = 1000000; // cycles; about 1/10th second in qemu.
  volatile uint64 * mtime=(uint64 *)0x200bff8;
  volatile uint64 * timecmp=(uint64 *)0x2004000;
  *(uint64*)timecmp = *(uint64*)mtime + interval;

  // prepare information in scratch[] for timervec.
  // scratch[0..2] : space for timervec to save registers.
  // scratch[3] : address of CLINT MTIMECMP register.
  // scratch[4] : desired interval (in cycles) between timer interrupts.
  uint64 *scratch = &timer_scratch[id][0];
  scratch[3] = (uint64)timecmp;
  scratch[4] = interval;
  w_mscratch((uint64)scratch);

  // set the machine-mode trap handler.
  // w_mtvec((uint64)timervec);

  // enable machine-mode interrupts.
  // w_mstatus(r_mstatus() | MSTATUS_MIE);

  // enable machine-mode timer interrupts.
  // w_mie(r_mie() | MIE_MTIE);
    clear_csr(mip, MIP_STIP);
    set_csr(mie, MIP_MTIP);
}


void
start(uintptr_t hartid, uintptr_t dtb)
{
  init_dtb(dtb);
  printm("stack0 %p\n",stack0);
  // set M Previous Privilege mode to Supervisor, for mret.
  unsigned long x = r_mstatus();
  x &= ~MSTATUS_MPP_MASK;
  x |= MSTATUS_MPP_S;
  w_mstatus(x);

  // set M Exception Program Counter to main, for mret.
  // requires gcc -mcmodel=medany
  w_mepc((uint64)main);

  // disable paging for now.
  w_satp(0);

  w_mtvec((uint64)mtrapvec);

  // delegate all interrupts and exceptions to supervisor mode.
  delegate_traps();

 
  w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);

  // ask for clock interrupts.
  timerinit();

  // keep each CPU's hartid in its tp register, for cpuid().
  int id = r_mhartid();
  w_tp(id);
  // switch to supervisor mode and jump to main().
  asm volatile("mret");
}


